package storage.v1;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.onpositive.mediawiki.propertyparser.TextParagraphParser;
import net.sourceforge.jwbf.core.actions.util.ActionException;
import net.sourceforge.jwbf.core.actions.util.ProcessException;
import net.sourceforge.jwbf.mediawiki.actions.queries.CategoryMembersFull;
import net.sourceforge.jwbf.mediawiki.bots.MediaWikiBot;
import net.sourceforge.jwbf.mediawiki.contentRep.CategoryItem;

public class RawTextReferencesStorage {
	
	private String localPath = "C:/wikistorage/" ;
	private String storageFilePath = "C:/wikistorage/storage.xml" ;
	private String globalPath = "http://en.wikipedia.org/w/" ;
	
	MediaWikiBot w = null ;
		
	private static final String NODE_NAME_ROOT = "reference-storage" ; 
	private static final String NODE_NAME_REFERENCE = "reference" ;
	private static final String REFERENCE_ATTRIBUTE_NAME_LABEL = "id" ;
	private static final String REFERENCE_ATTRIBUTE_PATH_LABEL = "path" ;
	private static final String REFERENCE_ATTRIBUTE_TYPE_LABEL = "type" ;
	private static final String NODE_NAME_PARENT_REFERENCE = "parent" ;
	private static final String REFERENCE_CHILD_ELEMENT_LABEL = "child" ;
	private static final String NODE_NAME_CHILD_REFERENCE = "reference" ;
	
	
	HashMap<String,RawTextReference> storage ;
	
	public RawTextReferencesStorage(String path) {
		localPath  = path.trim() ;
		localPath += localPath.endsWith("/") ? "" : "/" ; 
		localPath += "raw text storage/";
		storageFilePath = localPath+"storage.xml";		
	}
	
	public String getContent( String id, int flags )
	{
		if( id == null )
			return null ;
		
		if( storage == null )
			loadStorage() ;
		
		String result = null ;
		if( (flags & CategoryStorage.FORCE_RELOAD_FLAG ) == 0 )
			result = loadLocal( id ) ;
	
		return result != null ? result : loadGlobal( id , flags ) ;		
	}	
	

	public String loadLocal( String id ){	
		
		
		RawTextReference rtRef = storage.get(id) ;
		if( rtRef == null )
			return null ;
		
		String filePath = rtRef != null ? rtRef.getFilePath() : null ;
		if( filePath == null )
			return null ;
			//filePath = convert( localPath + id+".txt" ) ;
		
		File file = new File( filePath ) ;
				
		
		
		String text = loadText( file );		
		return text ;
	}

	private String loadText(File file) {
		
		if( !file.exists() )
			return null;
		
		String text = null ;
		
		if( file.length() != 0 ){
			try {
				
				FileInputStream inputFileStream = new FileInputStream( file ) ;
				DataInputStream inputStream = new DataInputStream( inputFileStream ) ; 
				byte[] tmpByteArray = new byte[ (int) file.length() ] ;
				inputStream.readFully( tmpByteArray ) ;
				text = new String( tmpByteArray , "UTF-8" ) ;			
				
				
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		else
			text = "" ;
		return text;
	}
	
	


	public String loadGlobal( String id , int flags ) {
		
		String filePath = localPath + convert( id ) +".txt" ;
		File file = new File( filePath ) ;
		
		try {
			
			if( w == null ){
				try {
					w = new MediaWikiBot( globalPath );
				} catch (MalformedURLException e) {
					e.printStackTrace();
				}
			}
			
			String text = null ;
			if( (flags & CategoryStorage.FORCE_RELOAD_FLAG ) == 0 )
				text = loadText(file) ;			
		
			if( text == null  )
			{
				text = w.readContent( id ).getText();
			
				new File( localPath ).mkdirs() ;
					
				file.createNewFile() ;
				FileOutputStream outputStream = new FileOutputStream( file ) ;
				outputStream.write( text.getBytes("UTF-8") ) ;
				outputStream.close() ;
			}
			
			RawTextReference rtRef = storage.get( id ) ;
			if( rtRef == null )
				rtRef = new RawTextReference( id, "root", filePath ) ;
			else
				rtRef.setFilePath(filePath) ;
			
			CategoryMembersFull cmf = new CategoryMembersFull( w, id ) ;
			
			while ( cmf.hasNext() ) {
				
				CategoryItem ci = cmf.next();
				String title = ci.getTitle();
				String childName, childType ; 
				
				int ind = title.indexOf(':');
				if( ind < 0  ){
					childName = title ;
					childType = "page" ;
				} else {
					childName = title.substring(ind+1) ;
					childType = title.substring(0, ind) ;
				}
				
				childName = TextParagraphParser.parse( childName ) ;
				childName = childName.replace("&#039;", "'");
				
				if( childType.equals("page") )
				{
					String redirected = ParserV2.redirect( childName ) ;
					if( redirected != null )
						childName = redirected ;
				}
				
				RawTextReference childRef = storage.get( childName ) ;
				
				if( childRef == null ){
					childRef = new RawTextReference( childName, childType, null ) ;
					storage.put( childName, childRef ) ;
				}
				childRef.addParent( rtRef ) ;
				rtRef.addChild( childRef ) ;
			}			
			
			storage.put( id, rtRef ) ;
			
			return text;
		
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (ActionException e) {
			e.printStackTrace();
		} catch (ProcessException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null ;		
	}
	
	public Collection<String> getChildren( String id )
	{
		if( storage == null )
			loadStorage() ;
		
		RawTextReference rtRef = storage.get(id) ;
		if( rtRef == null )
			return new ArrayList<String>() ;
		
		Collection<String> result = rtRef.getChildrenId() ;
		return result != null ? result : new ArrayList<String>() ;
	}
	
	public Collection<String> getParents( String id )
	{
		if( storage == null )
			loadStorage() ;
		
		RawTextReference rtRef = storage.get(id) ;
		if( rtRef == null )
			return new ArrayList<String>() ;
		
		Collection<String> result = rtRef.getParentsId() ;
		return result != null ? result : new ArrayList<String>() ;
	}

	public boolean containsId( String id ) {
		
		if( storage == null )
			loadStorage() ;
		
		return id != null ? storage.containsKey(id) : false ;
	}

	public Set<String> getIdSet() {
		
		if( storage == null )
			loadStorage() ;

		return storage.keySet();
	}
	
	public int size() {
		
		if( storage == null )
			loadStorage() ;

		return storage.size() ;
	}

	public String getRootPath() {
		return localPath;
	}

	public void setRootPath(String rootPath) {
		this.localPath = rootPath;
	}
	
	private String convert(String s)
	{
		return s.replace( '/', '_') ;
	}
	
	private void loadStorage() {
		storage = new HashMap<String, RawTextReference>() ;
		
		DocumentBuilder builder ;		
		try {
			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() ;
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			return ;
		}
		
		Document document ;
		
		try {
			document = builder.parse( new FileInputStream(new File( this.storageFilePath ) ) ) ;
		} catch ( FileNotFoundException e ) {
			//e.printStackTrace();
			return;
		} catch (SAXException e) {
			e.printStackTrace();
			return;
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}

		Element root = document.getDocumentElement() ;
		NodeList rootList = root.getChildNodes() ;
		int l0 = rootList.getLength() ;
		for( int i = 0 ; i < l0 ; i++ )
		{
			Node n0 = rootList.item(i);
			if( n0 instanceof Element )
			{
				String elementName0 = n0.getNodeName() ;
				if( elementName0.equals( NODE_NAME_REFERENCE ) ){
					
					Element levelOneElement = (Element)n0 ;					
					
					String refName = levelOneElement.getAttribute( REFERENCE_ATTRIBUTE_NAME_LABEL ) ;
					String refType = levelOneElement.getAttribute( REFERENCE_ATTRIBUTE_TYPE_LABEL ) ;
					String refPath = levelOneElement.getAttribute( REFERENCE_ATTRIBUTE_PATH_LABEL ) ;
					
					
					RawTextReference rtRef = storage.get( refName ) ;
					if( rtRef == null ){
						rtRef = new RawTextReference( refName, refType, refPath ) ;
						storage.put( refName, rtRef ) ;
					}
					else{
						rtRef.setType(refType) ;
						rtRef.setFilePath( refPath ) ;
					}
					
					NodeList fileElementList = levelOneElement.getChildNodes() ;
					int l1 = fileElementList.getLength() ;
					for( int j = 0 ; j < l1 ; j++ )
					{
						Node n1 = fileElementList.item(j);
						if( n1 instanceof Element )
						{
							Element fileElement = (Element)n1 ;
							String refVal = fileElement.getAttribute(NODE_NAME_CHILD_REFERENCE) ;
							RawTextReference relativeRef = storage.get( refVal ) ;
							if( relativeRef == null ){
								relativeRef = new RawTextReference() ;
								relativeRef.setName( refVal ) ;
								storage.put( refVal, relativeRef ) ;
							}
							
							String elementName1 = n1.getNodeName() ;							
							if( elementName1.equals(REFERENCE_CHILD_ELEMENT_LABEL) )
							{
								rtRef.addChild( relativeRef ) ;
								relativeRef.addParent( rtRef ) ;
							}
							if( elementName1.equals(NODE_NAME_PARENT_REFERENCE) )
							{
								rtRef.addParent( relativeRef ) ;
								relativeRef.addChild( rtRef ) ;								
							}
						}
					}					
				}
			}
		}
	}
	
	public void saveStorage(){
		
		DocumentBuilder builder ;		
		try {
			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() ;
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			return;
		}
		
		Document newDocument = builder.newDocument() ;
		Element rootElement = newDocument.createElement( NODE_NAME_ROOT );
		newDocument.appendChild( rootElement ) ;
		
		for( RawTextReference rtRef : storage.values() )
		{
			if( rtRef == null )
				continue ;
			
			Element refElement = newDocument.createElement( NODE_NAME_REFERENCE );
			refElement.setAttribute( REFERENCE_ATTRIBUTE_NAME_LABEL , rtRef.getName() ) ;
			refElement.setAttribute( REFERENCE_ATTRIBUTE_TYPE_LABEL , rtRef.getType() ) ;
			refElement.setAttribute( REFERENCE_ATTRIBUTE_PATH_LABEL , rtRef.getFilePath() ) ;
			
			for( String prntRefId : rtRef.getParentsId() )
			{
				Element cldElement = newDocument.createElement( NODE_NAME_PARENT_REFERENCE );
				cldElement.setAttribute( NODE_NAME_CHILD_REFERENCE, prntRefId ) ;
				refElement.appendChild( cldElement ) ;
			}
			
			for( String chldRefId : rtRef.getChildrenId() )
			{
				Element cldElement = newDocument.createElement( REFERENCE_CHILD_ELEMENT_LABEL );
				cldElement.setAttribute(NODE_NAME_CHILD_REFERENCE, chldRefId ) ;
				refElement.appendChild( cldElement ) ;
			}
			rootElement.appendChild( refElement ) ;					
		}
		
		Transformer transformer ;
		try {
			transformer =  TransformerFactory.newInstance().newTransformer() ;
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
			return ;
		} catch (TransformerFactoryConfigurationError e){
			e.printStackTrace();
			return ;
		}
		transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
		
		Element root = newDocument.getDocumentElement() ;
		StreamResult sr = new StreamResult( new File( storageFilePath ) ) ;
		try {
			transformer.transform( new DOMSource(root),  sr ) ;
		} catch (TransformerException e) {
			e.printStackTrace();
			return ;
		}	
	}
}
